/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "local-include/reg.h"
#include <cpu/cpu.h>
#include <cpu/ifetch.h>
#include <cpu/decode.h>

#define R(i) gpr(i)
#define Mr vaddr_read
#define Mw vaddr_write

enum {
  TYPE_I, TYPE_U, TYPE_S,TYPE_R,TYPE_J,TYPE_B,
  TYPE_N, // none
};

#define src1R() do { *src1 = R(rs1); } while (0)
#define src2R() do { *src2 = R(rs2); } while (0)
#define immI() do { *imm = SEXT(BITS(i, 31, 20), 12); } while(0)
#define immU() do { *imm = SEXT(BITS(i, 31, 12), 20) << 12; } while(0)
#define immS() do { *imm = (SEXT(BITS(i, 31, 25), 7) << 5) | BITS(i, 11, 7); } while(0)
#define immJ() do { *imm = SEXT(  ((BITS(i, 31, 31)<<20) |   (BITS(i, 30, 21)<<1) |   (BITS(i, 20, 20)<<11) |   (BITS(i, 19, 12)<<12)   ), 21); } while(0)
#define immB() do { *imm = SEXT(((BITS(i, 31, 31)<<12) | (BITS(i, 30, 25)<< 5) | (BITS(i, 11, 8)<<1) | (BITS(i, 7, 7)<<11) ), 13); } while(0)
//0011111 01010 01111 000 11000 01000 11
static void decode_operand(Decode *s, int *dest, word_t *src1, word_t *src2, word_t *imm, int type) {
  uint32_t i = s->isa.inst.val;
  int rd  = BITS(i, 11, 7);
  int rs1 = BITS(i, 19, 15);
  int rs2 = BITS(i, 24, 20);
  *dest = rd;
  switch (type) {
    case TYPE_I: src1R();          immI(); break;
    case TYPE_U:                   immU(); break;
    case TYPE_S: src1R(); src2R(); immS(); break;
    case TYPE_R: src1R(); src2R();         break;
    case TYPE_J:                   immJ(); break;
    case TYPE_B: src1R(); src2R(); immB(); break;
  }
}

#define FRING_BUFF_SIZE 512
struct _FRing {
  word_t pc;
  char *name;
  int level;
  enum _FType{
    NONE = 0,
    CALL,
    RET
  } type;
} fring_buff[FRING_BUFF_SIZE] = {0};
int fring_buff_index = 0;
int fring_level = 0;
void fring_save_fun(word_t pc,enum _FType type) {
  char *get_fun_name(word_t pc);
  if(type == CALL) fring_level++;
  fring_buff[fring_buff_index%FRING_BUFF_SIZE].type = type;
  fring_buff[fring_buff_index%FRING_BUFF_SIZE].pc = pc;
  fring_buff[fring_buff_index%FRING_BUFF_SIZE].level = fring_level;
  fring_buff[fring_buff_index%FRING_BUFF_SIZE].name = get_fun_name(pc);
  if(type == RET) {
    uint32_t idx = fring_buff_index%FRING_BUFF_SIZE;
    for (int i = 0; i < FRING_BUFF_SIZE; i++)
    {
        idx --;
        idx = idx%FRING_BUFF_SIZE;
        if(fring_buff[idx].type == CALL && fring_buff[idx].level == fring_level) {
          fring_buff[fring_buff_index%FRING_BUFF_SIZE].name = fring_buff[idx].name;
          break;
        }
    }
    fring_level--;
    
  }
  fring_buff_index ++;
}
void print_fring_buff(void) {
  printf("fring buff:\n");
  int func_cnt = fring_buff_index;
  int idx = 0;
  if(func_cnt > FRING_BUFF_SIZE) {
    func_cnt = FRING_BUFF_SIZE;
    idx = fring_buff_index;
  }
  while (func_cnt--)
  {
    struct _FRing fring = fring_buff[idx%FRING_BUFF_SIZE];
    for (int i = 0; i < fring.level; i++)
    {
      printf("│ ");
    }
    if(fring.type == CALL) printf("┌─call ");
    else printf("└─ret  ");
    printf("[%s@" FMT_WORD "]\n",fring.name,fring.pc);
    idx++;
  }
  
}
#define rd_x1_x5 (dest == 1 || dest == 5)
#define rs1_x1_x5 (BITS((s)->isa.inst.val, 19, 15) == 1 || BITS((s)->isa.inst.val, 19, 15) == 5)
#define rd_eq_rs1 (BITS((s)->isa.inst.val, 19, 15) == dest)
#define FTRACE_JALR()  do\
                  {\
                    if((!rd_x1_x5&&rs1_x1_x5)) fring_save_fun(s->dnpc,RET);  \
                    else if((rd_x1_x5&&!rs1_x1_x5) || (rd_x1_x5&&rs1_x1_x5&&rd_eq_rs1)) fring_save_fun(s->dnpc,CALL);\
                    else if(rd_x1_x5&&rs1_x1_x5&&!rd_eq_rs1) fring_save_fun(s->dnpc,RET); \
                    else if((rd_x1_x5&&rs1_x1_x5&&rd_eq_rs1)) { fring_save_fun(src1,RET); fring_save_fun(R(dest),CALL);  }\
                  }while(0)
#define FTRACE_JAL()  do\
                  {\
                    if((rd_x1_x5)) fring_save_fun(s->dnpc,CALL);\
                  }while(0)

static int decode_exec(Decode *s) {
  int dest = 0;
  word_t src1 = 0, src2 = 0, imm = 0;
  s->dnpc = s->snpc;

  int rs1 = BITS(s->isa.inst.val, 19, 15);
  // int rs2 = BITS(s->isa.inst.val, 24, 20);

#define INSTPAT_INST(s) ((s)->isa.inst.val)
#define INSTPAT_MATCH(s, name, type, ... /* execute body */ ) { \
  decode_operand(s, &dest, &src1, &src2, &imm, concat(TYPE_, type)); \
  __VA_ARGS__ ; \
}

  INSTPAT_START();
  //RV64I
  INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc  , U, R(dest) = s->pc + imm);
  INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui    , U, R(dest) =  imm);
  INSTPAT("??????? ????? ????? 000 ????? 00110 11", addiw  , I, R(dest) =  SEXT(src1+imm,32));
  INSTPAT("0000000 ????? ????? 000 ????? 01110 11", addw   , R, R(dest) =  SEXT(src1+src2,32));
  INSTPAT("??????? ????? ????? 011 ????? 00000 11", ld     , I, R(dest) =  Mr(src1 + imm, 8));
  INSTPAT("??????? ????? ????? 110 ????? 00000 11", lwu    , I, R(dest) =  Mr(src1 + imm, 4));
  INSTPAT("??????? ????? ????? 011 ????? 01000 11", sd     , S, Mw(src1 +  imm, 8, src2));
  INSTPAT("000000? ????? ????? 001 ????? 00100 11", slli   , I, R(dest) =  src1 << BITS(imm,5,0));
  INSTPAT("000000? ????? ????? 001 ????? 00110 11", slliw  , I, R(dest) =  SEXT(src1 << BITS(imm,4,0),32));
  INSTPAT("0000000 ????? ????? 001 ????? 01110 11", sllw   , R, R(dest) =  SEXT(src1 << BITS(src2,4,0),32));
  INSTPAT("010000? ????? ????? 101 ????? 00100 11", srai   , I, R(dest) =  ((sword_t)src1) >> BITS(imm,5,0));
  INSTPAT("0100000 ????? ????? 101 ????? 00110 11", sraiw  , I, R(dest) =  SEXT((((int32_t)src1) >> BITS(imm,4,0)),32));
  INSTPAT("0100000 ????? ????? 101 ????? 01110 11", sraw   , R, R(dest) =  SEXT((((int32_t)src1) >> BITS(src2,4,0)),32));
  INSTPAT("000000? ????? ????? 101 ????? 00100 11", srli   , I, R(dest) =  src1 >> BITS(imm,5,0));
  INSTPAT("000000? ????? ????? 101 ????? 00110 11", srliw  , I, R(dest) =  SEXT(BITS(src1,31,0) >> BITS(imm,4,0),32));
  INSTPAT("0000000 ????? ????? 101 ????? 01110 11", srlw   , R, R(dest) =  SEXT(BITS(src1,31,0) >> BITS(src2,4,0),32));
  INSTPAT("0100000 ????? ????? 000 ????? 01110 11", subw   , R, R(dest) =  SEXT(BITS(src1,31,0)-BITS(src2,31,0),32));
  //RV64M
  INSTPAT("0000001 ????? ????? 101 ????? 01110 11", DIVUW  , R, if(BITS(src2,31,0) == 0) R(dest) = BITS(-1ull,31,0); else R(dest) =  SEXT(BITS(src1,31,0) / BITS(src2,31,0),32));
  INSTPAT("0000001 ????? ????? 100 ????? 01110 11", DIVW   , R, if(BITS(src2,31,0) == 0) R(dest) = SEXT(BITS(-1ull,31,0),32); else R(dest) =  SEXT(((int32_t)BITS(src1,31,0)) / ((int32_t)BITS(src2,31,0)),32) );
  INSTPAT("0000001 ????? ????? 111 ????? 01110 11", REMUW  , R, if(BITS(src2,31,0) == 0) R(dest) = BITS(src1,31,0); else R(dest) =  SEXT(BITS(src1,31,0) % BITS(src2,31,0),32));
  INSTPAT("0000001 ????? ????? 110 ????? 01110 11", REMW   , R, if(BITS(src2,31,0) == 0) R(dest) = SEXT(BITS(src1,31,0),32); else R(dest) =  SEXT(((int32_t)BITS(src1,31,0)) % ((int32_t)BITS(src2,31,0)),32) );
  INSTPAT("0000001 ????? ????? 000 ????? 01110 11", MULW   , R, R(dest) =  SEXT(((int32_t)BITS(src1,31,0)) * ((int32_t)BITS(src2,31,0)),32) );
  //RV32M
  INSTPAT("0000001 ????? ????? 100 ????? 01100 11", DIV    , R, if(src2 == 0) R(dest) = -1ull; else R(dest) =  (sword_t)src1 / (sword_t)src2 );
  INSTPAT("0000001 ????? ????? 101 ????? 01100 11", DIVU   , R, if(src2 == 0) R(dest) = -1ull; else R(dest) =  src1 / src2  );
  INSTPAT("0000001 ????? ????? 111 ????? 01100 11", REMU   , R, if(src2 == 0) R(dest) = src1; else R(dest) =  src1 % src2 );
  INSTPAT("0000001 ????? ????? 110 ????? 01100 11", REM    , R, if(src2 == 0) R(dest) = src1; else R(dest) =  ((sword_t)src1) % ((sword_t)src2) );
  INSTPAT("0000001 ????? ????? 000 ????? 01100 11", MUL    , R, R(dest) =  (src1) * (src2) );
  INSTPAT("0000001 ????? ????? 001 ????? 01100 11", MULH   , R,  __int128 x,y,res; x = (__int128)((sword_t)src1); y = (__int128)((sword_t)src2); res = x*y; R(dest) = (sword_t)(res >> 64) );
  INSTPAT("0000001 ????? ????? 010 ????? 01100 11", MULHSU , R,  __int128 x,res; unsigned __int128 y; x = (__int128)((sword_t)src1); y = (unsigned __int128)((word_t)src2); res = x*y; R(dest) = (sword_t)(res >> 64) );
  INSTPAT("0000001 ????? ????? 011 ????? 01100 11", MULHU  , R,  unsigned __int128 x,y,res; x = (unsigned __int128)((word_t)src1); y = (unsigned __int128)((word_t)src2); res = x*y; R(dest) = (word_t)(res >> 64) );
  //RV32I
  INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add    , R, R(dest) =  src1+src2);
  INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi   , I, R(dest) =  src1+imm);
  INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and    , R, R(dest) =  src1&src2);
  INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi   , I, R(dest) =  src1&imm);
  INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq    , B, s->dnpc =  src1==src2?s->pc+imm:s->pc+4);
  INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge    , B, s->dnpc =  ((sword_t)src1)>=((sword_t)src2)?s->pc+imm:s->pc+4);
  INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu   , B, s->dnpc =  src1>=src2?s->pc+imm:s->pc+4);
  INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt    , B, s->dnpc =  ((sword_t)src1)<((sword_t)src2)?s->pc+imm:s->pc+4);
  INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu   , B, s->dnpc =  src1<src2?s->pc+imm:s->pc+4);
  INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne    , B, s->dnpc =  src1!=src2?s->pc+imm:s->pc+4);
  INSTPAT("0000000 00000 00000 000 00000 11100 11", ecall  , N, s->dnpc =  isa_raise_intr(MCAUSE_ECALL_M,s->pc) );
  INSTPAT("??????? ????? ????? 000 ????? 00011 11", fence  , N, printf("fence\n"));
  INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal    , J, R(dest) =  s->pc+4; s->dnpc = s->pc+imm; FTRACE_JAL());
  INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr   , I, R(dest) =  s->pc+4; s->dnpc = (src1+imm)&(~1ull); FTRACE_JALR());
  INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb     , I, R(dest) =  SEXT(Mr(src1+imm,1),8));
  INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu    , I, R(dest) =  Mr(src1+imm,1));
  INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh     , I, R(dest) =  SEXT(Mr(src1+imm,2),16));
  INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu    , I, R(dest) =  Mr(src1+imm,2));
  INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw     , I, R(dest) =  SEXT(Mr(src1+imm,4),32));
  INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or     , R, R(dest) =  src1|src2);
  INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori    , I, R(dest) =  src1|imm);
  INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb     , S, Mw(src1+imm,1,src2));
  INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh     , S, Mw(src1+imm,2,src2));
  INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw     , S, Mw(src1+imm,4,src2));
  INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll    , R, R(dest) =  src1 << BITS(src2,5,0));
  INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt    , R, R(dest) =  ((sword_t)src1)<((sword_t)src2));
  INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti   , I, R(dest) =  ((sword_t)src1)<((sword_t)imm));
  INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu  , I, R(dest) =  src1<imm);
  INSTPAT("??????? ????? ????? 011 ????? 01100 11", sltu   , R, R(dest) =  src1<src2);
  INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra    , R, R(dest) =  ((sword_t)src1) >> BITS(src2,5,0));
  INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl    , R, R(dest) =  (src1) >> BITS(src2,5,0));
  INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub    , R, R(dest) =  src1-src2);
  INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor    , R, R(dest) =  src1^src2);
  INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori   , I, R(dest) =  src1^imm);
  INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0
  //Zicsr
  INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw  , I, if(dest != 0) R(dest) = csr_read(BITS(imm,11,0)); csr_write(BITS(imm,11,0),src1) );
  INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs  , I, R(dest) = csr_read(BITS(imm,11,0)); csr_set_bits(BITS(imm,11,0),src1) );
  INSTPAT("??????? ????? ????? 011 ????? 11100 11", csrrc  , I, R(dest) = csr_read(BITS(imm,11,0)); csr_clear_bits(BITS(imm,11,0),src1) );
  INSTPAT("??????? ????? ????? 101 ????? 11100 11", csrrwi , I, if(dest != 0) R(dest) = csr_read(BITS(imm,11,0)); csr_write(BITS(imm,11,0),rs1) );
  INSTPAT("??????? ????? ????? 110 ????? 11100 11", csrrsi , I, csr_read(BITS(imm,11,0)); if(rs1 != 0) csr_set_bits(BITS(imm,11,0),rs1) );
  INSTPAT("??????? ????? ????? 111 ????? 11100 11", csrrci , I, csr_read(BITS(imm,11,0)); if(rs1 != 0) csr_clear_bits(BITS(imm,11,0),rs1) );
  //Trap-Return Instructions
  INSTPAT("0011000 00010 00000 000 00000 11100 11", mret   , N, word_t mpie=csr_read(MSTATUS_ADDR)&MSATUS_MPIE_MASK; if(mpie) csr_set_bits(MSTATUS_ADDR,MSATUS_MIE_MASK);else csr_clear_bits(MSTATUS_ADDR,MSATUS_MIE_MASK); csr_set_bits(MSTATUS_ADDR,MSATUS_MPIE_MASK); s->dnpc = csr_read(MEPC_ADDR)); // R(10) is $a0

  INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv    , N, INV(s->pc));
  INSTPAT_END();

  R(0) = 0; // reset $zero to 0

  return 0;
}
#define IRING_BUFF_SIZE 128
struct _IRing {
  uint32_t inst;
  word_t pc;
  word_t snpc;
} iring_buff[IRING_BUFF_SIZE];

int iring_buff_index = 0;
void iring_buff_save_inst(uint32_t inst,word_t pc,word_t snpc) {
  iring_buff[iring_buff_index%IRING_BUFF_SIZE].inst = inst;
  iring_buff[iring_buff_index%IRING_BUFF_SIZE].pc = pc;
  iring_buff[iring_buff_index%IRING_BUFF_SIZE].snpc = snpc;
  iring_buff_index ++;
}

void print_iring_buff(void) {
  printf("iring buff:\n");
  int inst_cnt = iring_buff_index;
  int inst_start = 0;
  if(inst_cnt > IRING_BUFF_SIZE) {
    inst_cnt = IRING_BUFF_SIZE;
    inst_start = (iring_buff_index)%IRING_BUFF_SIZE;
  }
  char buf[128];
  void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte);
  for (int i = 0; i < inst_cnt; i++)
  {
    char *p = buf;
    struct _IRing *s = &iring_buff[inst_start++];
    inst_start %= IRING_BUFF_SIZE;
    if(inst_start == iring_buff_index%IRING_BUFF_SIZE) printf("  ==>  ");
    else printf("       ");
    printf( FMT_WORD ":", s->pc);
    int ilen = s->snpc - s->pc;
    uint8_t *inst = (uint8_t *)&s->inst;
    for (int j = ilen - 1; j >= 0; j --) {
      printf(" %02x", inst[j]);
    }
    int ilen_max = MUXDEF(CONFIG_ISA_x86, 8, 4);
    int space_len = ilen_max - ilen;
    if (space_len < 0) space_len = 0;
    space_len = space_len * 3 + 1;
    while (space_len--)
    {
      printf(" ");
    }
#ifdef CONFIG_ITRACE
    disassemble(p, buf + sizeof(buf) - p,
        MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), (uint8_t *)&s->inst, ilen);
#endif
    printf("%s\n",p);
  }
  
  

}

static bool is_fetch_state = false;
void set_fetch_state(bool is_fetch) {
  is_fetch_state = is_fetch;
}
bool get_fetch_state(void) {
  return is_fetch_state;
}
int isa_exec_once(Decode *s) {
  set_fetch_state(true);
  s->isa.inst.val = inst_fetch(&s->snpc, 4);
#ifdef CONFIG_ITRACE
  iring_buff_save_inst(s->isa.inst.val,s->pc,s->snpc);
 #endif
  set_fetch_state(false);
  return decode_exec(s);
}
